/**
 *  \file probMonAirportRhapsody.c (implementation file)
 *
 *  \brief Problem name: Airport rhapsody.
 *
 *  \brief Concept: António Rui Borges
 *
 *  Synchronization based on monitors.
 *  Both threads and the monitor are implemented using pthread library which enables the creation of a
 *  monitor of the Lampson / Redell type.
 *
 *  Generator thread of the intervening entities.
 *
 *  Upon execution, one parameter is requested:
 *    \li name of the logging file.
 *
 *  \author António Rui Borges - December 2013
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <pthread.h>

#include "probConst.h"
#include "probDataStruct.h"
#include "cam.h"
#include "queue.h"
#include "logging.h"
#include "monAirportRhapsodyIDS.h"
#include "monAirportRhapsodyPo.h"
#include "monAirportRhapsodyPa.h"
#include "monAirportRhapsodyDr.h"

/** \brief logging file name */
char nFic[51];

/** \brief porter thread return status */
int statusPO;

/** \brief passengers thread return status array */
int statusPA[N];

/** \brief bus driver thread return status array */
int statusDR;

/** \brief porter routine */
static void *porter (void *didPo);

/** \brief passenger routine */
static void *passenger (void *didPa);

/** \brief driver routine */
static void *driver (void *dummy);

/**
 *  \brief Main thread.
 *
 *  Its role is starting the simulation by generating the intervening entities threads (porter, passengers and bus
 *  driver), waiting for their termination and presenting the final report.
 */

int  main (void)
{
  FILE *fic;                                                                                      /* file descriptor */
  int t;                                                                               /* keyboard reading test flag */
  char opt;                                                                                                /* answer */
  unsigned int n;                                                                               /* counting variable */
  pthread_t tidPO,                                                          /* porter internal thread identification */
            tidPA[N],                                             /* passengers internal thread identification array */
            tidDR;                                                      /* bus driver internal thread identification */
  unsigned int aidPA[N];                               /* passengers application defined thread identification array */
  int *status_p;                                                                      /* pointer to execution status */

  /* getting log file name */

  do
  { do
    { printf ("\nLog file name? ");
      t = scanf ("%20[^\n]", nFic);
      scanf ("%*[^\n]");
      scanf ("%*c");
    } while (t == 0);
    fic = fopen (nFic, "r");
    if (fic != NULL)
       { fclose (fic);
         printf ("There is already a file with this name! ");
         do
         { printf ("Overwrite? ");
           scanf ("%c", &opt);
           if (opt != '\n')
              { scanf ("%*[^\n]");
                scanf ("%*c");
              }
         } while ((opt == '\n') || ((opt != 'Y') && (opt != 'y') &&
                                    (opt != 'N') && (opt != 'n')));
         if ((opt == 'Y') || (opt == 'y')) break;
       }
  } while (fic != NULL);

  /* initializing passengers application defined thread identification array */

  for (n = 0; n < N; n++)
    aidPA[n] = n;

  /* generation of the intervening entities threads */

  if (pthread_create (&tidPO, NULL, porter, NULL) != 0)
     { perror ("error on the create operation for the porterer thread");
       return EXIT_FAILURE;
     }
  if (pthread_create (&tidDR, NULL, driver, NULL) != 0)
      { perror ("error on the create operation for the bus driver thread");
        return EXIT_FAILURE;
      }
  for (n = 0; n < N; n++)
    if (pthread_create (&tidPA[n], NULL, passenger, &aidPA[n]) != 0)
       { perror ("error on the create operation for a passenger thread");
         return EXIT_FAILURE;
       }

  /* waiting for the termination of the porter, passengers and the bus driver threads */

  printf ("\nFinal report\n");
  if (pthread_join (tidPO, (void *) &status_p) != 0)
     { perror ("error on waiting for the porter thread");
       return EXIT_FAILURE;
     }
  printf ("porter thread has terminated: ");
  printf ("its status was %d\n", *status_p);
  for (n = 0; n < N; n++)
  { if (pthread_join (tidPA[n], (void *) &status_p) != 0)
       { perror ("error on waiting for the passenger thread");
         return EXIT_FAILURE;
       }
    printf ("passenger thread with id %u, has terminated: ", n);
    printf ("its status was %d\n", *status_p);
  }
  if (pthread_join (tidDR, (void *) &status_p) != 0)
     { perror ("error on waiting for the bus driver thread");
       return EXIT_FAILURE;
     }
  printf ("bus driver thread has terminated: ");
  printf ("its status was %d\n", *status_p);

  /* print final report */

  finalReport (nFic, &fSt);

  return EXIT_SUCCESS;

} /* end of thread main */

/**
 *  \brief Thread porter.
 *
 *  Its role is to simulate the life cycle of the porter.
 *
 *  \param dummy pointer to a meaningless data structure (required for compatibility reasons with pthread_create)
 */

static void *porter (void *dummy)
{
  unsigned int k;                                                                                   /* flight number */
  BAG bag;                                                                       /* piece of luggage to be processed */

  for (k = 0; k < K; k++)
  { takeARest (k);                                                           /* the porter waits for a plane to land */
    while (tryToCollectABag (k, &bag))          /* the porter checks if there is still any luggage to collect at the
                                                                          plane's hold, if it is so he picks a piece */
      carryItToAppropriateStore (k, &bag);                         /* the porter carries it to the appropriate store
                                                                                 (luggage conveyor belt / storeroom) */
    noMoreBagsToCollect (k);                                                /* the porter goes back to the rest room */
  }
  statusPO = EXIT_SUCCESS;
  pthread_exit (&statusPO);
}

/**
 *  \brief Thread passenger pair.
 *
 *  Its role is to simulate the life cycle of a passenger.
 *
 *  \param didP pointer to the application defined passenger identification
 */

static void *passenger (void *didP)
{
  unsigned int k;                                                                                   /* flight number */
  unsigned int p;                                                                        /* passenger identification */
  unsigned int stat;                                                                      /* status of the operation */

  p = *((unsigned int *) didP);

  for (k = 0; k < K; k++)
    switch (whatShouldIDo (k, p))                                          /* the passenger decides on her next move */
    { case FDBTC:                                /* she has arrived to her final destination and has bags to collect */
                                /* the passenger goes to the luggage collection point to pick up her bags one by one */
        while ((stat = goCollectABag (k, p)) == NO);
        if (stat == MB)                                                     /* the passenger checks for missing bags */
           reportMissingBags (k, p);          /* the passenger go to the baggage reclaim office to fill the form for
                                                                                                        missing bags */
        goHome (k, p);                                                           /* the passenger leaves the airport */
        break;
      case FDNBTC:                            /* she has arrived to her final destination and has no bags to collect */
        goHome (k, p);                                                           /* the passenger leaves the airport */
        break;
      case INTRAN:                                                                              /* she is in transit */
        takeABus (k, p);                             /* the passenger goes to the arrival transfer terminal to queue
                                                                          for taking a bus to the departure terminal */
        enterTheBus (k, p);                       /* the passenger goes on board of the bus as it starts its journey */
        leaveTheBus (k, p);      /* the passenger comes out of the bus as it reaches the departure transfer terminal */
        prepareNextLeg (k, p);              /* the passenger enters the departure and does the check in for the next
                                                                                                  leg of the journey */
        break;
    }

  statusPA[p] = EXIT_SUCCESS;
  pthread_exit (&statusPA[p]);
}

/**
 *  \brief Bus driver.
 *
 *  Its role is to simulate the life cycle of the bus driver.
 *
 *  \param dummy pointer to a meaningless data structure (required for compatibility reasons with pthread_create)
 */

static void *driver (void *dummy)
{
  while (!hasDaysWorkEnded ())
  { announcingBusBoarding ();             /* the driver invites the passengers forming the queue to board the bus up
                                                     to it is packed full or there is at least one passenger waiting */
    goToDepartureTerminal ();                                  /* the driver takes the bus to the departure terminal */
    parkTheBusAndLetPassOff ();             /* the driver parks the bus at the terminal and let the passengers leave */
    goToArrivalTerminal ();                                 /* the driver takes the bus back to the arrival terminal */
    parkTheBus ();                                                               /* the driver parks at the terminal */
  }

  statusDR = EXIT_SUCCESS;
  pthread_exit (&statusDR);
}
